<?php

/**
 * @user magein
 * @date 2023/12/1 13:32
 */

namespace magein\sms\platform;

use magein\utils\Result;
use magein\sms\SmsCode;
use Psr\Http\Message\ResponseInterface;

class Tencent extends Platform
{
    protected $app_id = '';

    protected $secret_id = '';

    protected $secret_key = '';

    protected $signature = '';

    /**
     * @return string
     */
    public function version(): string
    {
        return 'v3';
    }

    protected function transErrorCode($error)
    {
        $code = $error['Code'];
        $message = $error['Message'];

        switch ($code) {
            case 'InvalidParameter':
                $message = '参数错误（包括参数格式、类型等错误）。';
                break;
            case 'InvalidParameterValue':
                $message = '参数取值错误。';
                break;
            case 'AuthFailure.InvalidAuthorization':
                $message = '请求头部的 Authorization 不符合腾讯云标准。';
                break;
            case 'AuthFailure.SecretIdNotFound';
                $message = '密钥不存在。请在 控制台 检查密钥是否已被删除或者禁用，如状态正常，请检查密钥是否填写正确，注意前后不得有空格。';
                break;
            case 'FailedOperation.SignatureIncorrectOrUnapproved';
                $message = '签名未审批或格式错误。';
                break;
            case 'MissingParameter';
                $message = '缺少参数：' . $message;
                break;
        }

        return $code . ' : ' . $message;
    }

    /**
     * @return Result
     */
    public function code(): Result
    {
        $code_template_id = $this->target->getTemplateId();
        if (empty($code_template_id)) {
            $this->target->setTemplateId($this->config['code_template_id'] ?? '');
        }
        return $this->send();
    }

    /**
     * @return Result
     */
    public function send(): Result
    {

        $this->request();

        $content = $this->toArray();

        if (empty($content)) {
            return Result::error($this->error ?: '短信平台请求异常', SmsCode::SMS_PLATFORM_EXCEPTION);
        }

        $response = $content['Response'] ?? [];

        $error = $response['Error'] ?? [];
        if ($error) {
            return Result::error($this->transErrorCode($error), SmsCode::SMS_SEND_FAIL);
        }

        /**
         * 'Response' =>
         * array (size=2)
         * 'SendStatusSet' =>
         * array (size=1)
         * 0 =>
         * array (size=7)
         *  'SerialNo' => string '3802:11427507617016881035244605' (length=31)
         *  'PhoneNumber' => string '+8615956946055' (length=14)
         *  'Fee' => int 1
         *  'SessionContext' => string '' (length=0)
         *  'Code' => string 'Ok' (length=2)
         *  'Message' => string 'send success' (length=12)
         *  'IsoCode' => string 'CN' (length=2)
         *  'RequestId' => string '1f615d17-06ee-4d46-901e-211d90b41e6b' (length=36)
         */

        $status = $response['SendStatusSet'] ?? [];

        $first = current($status);

        if ($first['Code'] === 'Ok') {
            return Result::success($first);
        }

        return Result::error($this->transErrorCode($first), SmsCode::SMS_SEND_FAIL);
    }

    /**
     * @param $params
     * @return void
     */
    public function init($params)
    {
        $this->app_id = $params['app_id'];
        $this->secret_id = $params['secret_id'];
        $this->secret_key = $params['secret_key'];
        $this->signature = $params['signature'];
    }

    /**
     * @return ResponseInterface|null
     */
    protected function request()
    {
        $host = 'sms.tencentcloudapi.com';
        $service = 'sms';
        $version = '2021-01-11';
        $action = 'SendSms';

        /**
         * 地区可选值
         * https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8
         * @var $region
         */
        $region = $this->target->getOther('region') ?: 'ap-nanjing';
        $timestamp = time();
        $algorithm = 'TC3-HMAC-SHA256';

        $http_request_method = 'POST';
        $canonical_uri = '/';
        $canonical_query_string = '';

        /**
         * 参考文档：https://cloud.tencent.com/document/product/382/52072
         */


        /**
         * 参与签名的头部信息 至少包含 host 和 content-type 两个头部
         * 拼接规则：
         * 1.头部 key 和 value 统一转成小写，并去掉首尾空格，按照 key:value\n 格式拼接；
         * 2.多个头部，按照头部 key（小写）的 ASCII 升序进行拼接。
         */
        $headers = [
            'host' => $host,
            'content-type' => 'application/json; charset=utf-8',
        ];
        ksort($headers);

        $canonical_headers = '';
        foreach ($headers as $key => $header) {
            $canonical_headers .= $key . ':' . $header . "\n";
        }

        $headers = [
            'content-type',
            'host',
        ];
        ksort($headers);

        /**
         * 参与签名的头部信息，说明此次请求有哪些头部参与了签名，和 CanonicalHeaders 包含的头部内容是一一对应的。content-type 和 host 为必选头部。
         * 拼接规则：
         * 1. 头部 key 统一转成小写；
         * 2. 多个头部 key（小写）按照 ASCII 升序进行拼接，并且以分号（;）分隔。
         */
        $signed_headers = implode(";", $headers);

        $phone = $this->target->getPhone();
        $phones = explode(',', $phone);
        if (!is_array($phones)) {
            $phones = [$phones];
        }

        foreach ($phones as $key => $phone) {
            $phones[$key] = $phone . '';
        }

        $body = [
            'PhoneNumberSet' => $phones,
            'SmsSdkAppId' => $this->app_id,
            'SignName' => $this->signature,
            'TemplateId' => $this->target->getTemplateId(),
        ];

        // 获取要发送的内容
        $content = $this->target->getContent();

        // 如果传递的是一个数组形式的字符串，则需要转化成数组
        $template_params = [];

        if ($content) {
            $content = json_decode($content, true);
            if (is_array($content)) {
                foreach ($content as $item) {
                    $template_params[] = $item;
                }
            } else {
                $template_params[] = $content;
            }
        }

        // 这里要注意传递的类型为数组且需要用双引号 验证码类一般是 1234、123456这种的整型数
        if ($template_params) {
            foreach ($template_params as $key => $value) {
                $template_params[$key] = $value . '';
            }
            $body['TemplateParamSet'] = $template_params;
        }

        $payload = json_encode($body, JSON_UNESCAPED_UNICODE);

        $hashed_request_payload = hash('SHA256', $payload);
        $canonical_request = $http_request_method . "\n"
            . $canonical_uri . "\n"
            . $canonical_query_string . "\n"
            . $canonical_headers . "\n"
            . $signed_headers . "\n"
            . $hashed_request_payload;

        $date = gmdate('Y-m-d', $timestamp);
        $credentialScope = $date . "/" . $service . "/tc3_request";
        $hashed_canonical_request = hash("SHA256", $canonical_request);
        $string_To_sign = $algorithm . "\n"
            . $timestamp . "\n"
            . $credentialScope . "\n"
            . $hashed_canonical_request;

        $secret_date = hash_hmac('SHA256', $date, 'TC3' . $this->secret_key, true);
        $secret_service = hash_hmac('SHA256', $service, $secret_date, true);
        $secret_signing = hash_hmac('SHA256', 'tc3_request', $secret_service, true);
        $signature = hash_hmac('SHA256', $string_To_sign, $secret_signing);

        $authorization = $algorithm
            . " Credential=" . $this->secret_id . "/" . $credentialScope
            . ", SignedHeaders=" . $signed_headers . ", Signature=" . $signature;

        return $this->post('https://' . $host, [
            'headers' => [
                'Authorization' => $authorization,
                'Content-Type' => 'application/json; charset=utf-8',
                'host' => $host,
                'X-TC-Action' => $action,
                'X-TC-Timestamp' => $timestamp,
                'X-TC-Version' => $version,
                'X-TC-Region' => $region,
            ],
            'body' => $payload
        ]);
    }
}